package org.Yiran.timetale_re.util.cooldown;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import net.minecraft.client.Minecraft;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.ForgeRegistries;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 客户端冷却管理器
 * <p>
 * 用于在客户端存储和管理物品冷却状态，支持持久化存储以在游戏重启后保持冷却状态。
 * 主要用于管理符文等物品的使用冷却时间。
 * </p>
 * 
 * @author Yiran
 * @version 1.0
 */
@OnlyIn(Dist.CLIENT)
public class ClientCooldownManager {
    
    // ==================== 数据存储 ====================
    
    /**
     * 存储客户端物品冷却时间的映射
     * <p>
     * 键为物品键，值为冷却结束时间（游戏刻）。
     * </p>
     */
    private static final Map<String, Long> clientCooldowns = new HashMap<>();

    /**
     * 存储符文类型映射，用于渲染
     * <p>
     * 键为物品类名，值为符文类型，用于在渲染时区分不同类型的符文。
     * </p>
     */
    private static final Map<String, String> runeTypeMap = new HashMap<>();

    // ==================== 配置常量 ====================
    
    /**
     * Gson实例用于序列化/反序列化
     * <p>
     * 使用美化打印格式，使生成的JSON文件更易读。
     * </p>
     */
    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();

    /**
     * 冷却数据文件路径
     * <p>
     * 冷却数据将保存在此文件中，以便在游戏重启后恢复。
     * </p>
     */
    private static final String COOLDOWN_DATA_FILE = "timetale_re_cooldowns.json";

    /**
     * 关闭钩子是否已注册
     * <p>
     * 用于确保关闭钩子只注册一次，避免重复注册。
     * </p>
     */
    private static boolean shutdownHookRegistered = false;

    // ==================== 初始化 ====================
    
    static {
        // 注册关闭钩子以确保数据在游戏关闭时保存
        registerShutdownHook();
    }

    // ==================== 冷却设置方法 ====================

    /**
     * 设置物品冷却时间
     * <p>
     * 将指定物品键的冷却结束时间记录到客户端冷却映射中。
     * 如果是符文物品，还会额外存储符文类型信息用于渲染。
     * 设置完成后会自动保存数据到文件。
     * </p>
     * 
     * @param itemKey 物品键
     * @param cooldownEnd 冷却结束时间（游戏刻）
     */
    public static void setCooldown(String itemKey, long cooldownEnd) {
        clientCooldowns.put(itemKey, cooldownEnd);

        // 如果是符文物品，额外存储符文类型信息
        if (itemKey.contains(":")) {
            String[] parts = itemKey.split(":");
            if (parts.length > 1) {
                runeTypeMap.put(parts[0], parts[1]); // 存储类名和符文类型
            }
        }

        // 保存数据到文件
        saveCooldownData();
    }

    /**
     * 获取符文类型（用于渲染）
     * <p>
     * 根据物品类名获取对应的符文类型，用于在渲染时区分不同类型的符文。
     * </p>
     * 
     * @param itemClassName 物品类名
     * @return 符文类型，如果未找到则返回空字符串
     */
    public static String getRuneType(String itemClassName) {
        return runeTypeMap.getOrDefault(itemClassName, "");
    }

    // ==================== 冷却检查方法 ====================

    /**
     * 检查物品是否在冷却中
     * <p>
     * 根据冷却类别、物品实例和物品堆生成物品键，然后检查该物品是否在冷却中。
     * </p>
     * 
     * @param category 冷却类别
     * @param item 物品实例
     * @param stack 物品堆（可为空）
     * @return 是否在冷却中
     */
    public static boolean isOnCooldown(String category, Item item, ItemStack stack) {
        String itemKey = getCooldownKey(category, item, stack);
        return isOnCooldown(itemKey);
    }

    /**
     * 检查物品是否在冷却中（使用完整键）
     * <p>
     * 直接使用物品键检查物品是否在冷却中。
     * 如果冷却已结束，会自动移除冷却记录并保存数据。
     * </p>
     * 
     * @param itemKey 物品键
     * @return 是否在冷却中
     */
    public static boolean isOnCooldown(String itemKey) {
        boolean isOnCooldown = clientCooldowns.containsKey(itemKey);

        if (isOnCooldown) {
            long currentTime = getCurrentTime();
            long cooldownEnd = clientCooldowns.get(itemKey);

            // 如果冷却已结束，移除冷却记录
            if (currentTime >= cooldownEnd) {
                clientCooldowns.remove(itemKey);
                saveCooldownData(); // 保存更新后的数据
                return false;
            }
        }
        return isOnCooldown;
    }

    // ==================== 冷却时间查询方法 ====================

    /**
     * 获取物品剩余冷却时间
     * <p>
     * 根据冷却类别、物品实例和物品堆生成物品键，然后获取该物品的剩余冷却时间。
     * </p>
     * 
     * @param category 冷却类别
     * @param item 物品实例
     * @param stack 物品堆
     * @return 剩余冷却时间（游戏刻），如果不在冷却中则返回0
     */
    public static long getRemainingCooldown(String category, Item item, ItemStack stack) {
        String itemKey = getCooldownKey(category, item, stack);
        return getRemainingCooldown(itemKey);
    }

    /**
     * 获取物品剩余冷却时间（使用完整键）
     * <p>
     * 直接使用物品键获取物品的剩余冷却时间。
     * 如果冷却已结束，会自动移除冷却记录并保存数据。
     * </p>
     * 
     * @param itemKey 物品键
     * @return 剩余冷却时间（游戏刻），如果不在冷却中则返回0
     */
    public static long getRemainingCooldown(String itemKey) {
        if (!clientCooldowns.containsKey(itemKey)) {
            return 0;
        }

        long currentTime = getCurrentTime();
        long cooldownEnd = clientCooldowns.get(itemKey);
        long remaining = cooldownEnd - currentTime;

        // 如果冷却已结束，移除冷却记录
        if (remaining <= 0) {
            clientCooldowns.remove(itemKey);
            saveCooldownData(); // 保存更新后的数据
            return 0;
        }
        return remaining;
    }

    // ==================== 辅助方法 ====================

    /**
     * 获取当前时间（游戏刻）
     * <p>
     * 获取当前游戏世界的时间，用于计算冷却时间。
     * </p>
     * 
     * @return 当前游戏时间（游戏刻）
     */
    private static long getCurrentTime() {
        Minecraft mc = Minecraft.getInstance();
        if (mc.level != null) {
            return mc.level.getGameTime();
        }
        return 0;
    }

    /**
     * 清理所有冷却数据（在玩家退出时调用）
     * <p>
     * 清空所有冷却数据和符文类型映射，并保存清空后的状态到文件。
     * </p>
     */
    public static void clearAllCooldowns() {
        clientCooldowns.clear();
        runeTypeMap.clear();
        saveCooldownData(); // 保存清空后的状态
    }

    /**
     * 获取冷却键
     * <p>
     * 根据冷却类别、物品实例和物品堆生成物品的唯一标识符。
     * 如果物品有NBT标签，则将其包含在冷却键中以区分不同的物品变体。
     * </p>
     * 
     * @param category 冷却类别
     * @param item 物品实例
     * @param stack 物品堆，用于生成基于NBT的键
     * @return 物品的唯一标识符
     */
    public static String getCooldownKey(String category, Item item, ItemStack stack) {
        String baseKey = category + ":" + Objects.requireNonNull(ForgeRegistries.ITEMS.getKey(item)).toString();

        // 如果物品有NBT标签，则将其包含在冷却键中
        if (stack != null && stack.hasTag() && stack.getTag() != null) {
            // 使用NBT的哈希码作为区分依据
            baseKey += ":" + stack.getTag().hashCode();
        }

        return baseKey;
    }

    // ==================== 数据持久化方法 ====================

    /**
     * 保存冷却数据到文件
     * <p>
     * 将当前的冷却数据和符文类型映射保存到JSON文件中，
     * 以便在游戏重启后恢复冷却状态。
     * </p>
     */
    public static void saveCooldownData() {
        try {
            Minecraft mc = Minecraft.getInstance();

            Path gameDir = mc.gameDirectory.toPath();
            Path dataFile = gameDir.resolve(COOLDOWN_DATA_FILE);

            // 创建要保存的数据结构
            Map<String, Object> data = new HashMap<>();
            data.put("cooldowns", clientCooldowns);
            data.put("runeTypes", runeTypeMap);

            // 写入文件
            String json = GSON.toJson(data);
            Files.write(dataFile, json.getBytes());
        } catch (Exception e) {
            // 忽略保存错误，不影响游戏运行
        }
    }

    /**
     * 从文件加载冷却数据
     * <p>
     * 从JSON文件中加载冷却数据和符文类型映射，
     * 恢复游戏重启前的冷却状态。
     * </p>
     */
    public static void loadCooldownData() {
        try {
            Minecraft mc = Minecraft.getInstance();

            Path gameDir = mc.gameDirectory.toPath();
            Path dataFile = gameDir.resolve(COOLDOWN_DATA_FILE);

            if (Files.exists(dataFile)) {
                String json = new String(Files.readAllBytes(dataFile));
                Map<String, Object> data = GSON.fromJson(json, new TypeToken<Map<String, Object>>(){}.getType());

                // 恢复冷却数据
                if (data.containsKey("cooldowns")) {
                    @SuppressWarnings("unchecked")
                    Map<String, Double> cooldowns = (Map<String, Double>) data.get("cooldowns");
                    clientCooldowns.clear();
                    for (Map.Entry<String, Double> entry : cooldowns.entrySet()) {
                        clientCooldowns.put(entry.getKey(), entry.getValue().longValue());
                    }
                }

                // 恢复符文类型数据
                if (data.containsKey("runeTypes")) {
                    runeTypeMap.clear();
                    @SuppressWarnings("unchecked")
                    Map<String, String> runeTypes = (Map<String, String>) data.get("runeTypes");
                    runeTypeMap.putAll(runeTypes);
                }
            }
        } catch (Exception e) {
            // 忽略加载错误，使用默认空数据
        }
    }

    // ==================== 系统方法 ====================

    /**
     * 注册关闭钩子以确保数据在游戏关闭时保存
     * <p>
     * 注册一个JVM关闭钩子，在游戏关闭时自动保存冷却数据。
     * </p>
     */
    private static void registerShutdownHook() {
        if (!shutdownHookRegistered) {
            Runtime.getRuntime().addShutdownHook(new Thread(ClientCooldownManager::saveCooldownData));
            shutdownHookRegistered = true;
        }
    }
}